package com.sky.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.MessageConstant;
import com.sky.context.BaseContext;
import com.sky.dto.*;
import com.sky.entity.*;
import com.sky.exception.OrderBusinessException;
import com.sky.mapper.AddressBookMapper;
import com.sky.mapper.OrderDetailMapper;
import com.sky.mapper.OrderMapper;
import com.sky.mapper.ShoppingCartMapper;
import com.sky.result.PageResult;
import com.sky.server.WebSocketServer;
import com.sky.service.OrderService;
import com.sky.vo.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author liudo
 * @version 1.0
 * @project sky-take-out
 * @date 2024/2/25 09:15:23
 */
@Slf4j
@Service
@AllArgsConstructor
public class OrderServiceImpl implements OrderService {

    private OrderMapper orderMapper;

    private OrderDetailMapper orderDetailMapper;

    private ShoppingCartMapper shoppingCartMapper;

    private AddressBookMapper addressBookMapper;

    private WebSocketServer webSocketServer;

    private HttpServletResponse httpServletResponse;

    @Override
    @Transactional
    public OrderSubmitVO submit(OrdersSubmitDTO ordersSubmitDTO) {
        // 查询购物车列表
        ShoppingCart shoppingCartQuery = new ShoppingCart();
        shoppingCartQuery.setUserId(BaseContext.getCurrentId());
        List<ShoppingCart> shoppingCartList = shoppingCartMapper.selectList(shoppingCartQuery);
        if (shoppingCartList.isEmpty()) {
            throw new OrderBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
        }
        // 查询地址信息
        AddressBook addressBook = addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());
        if (Objects.isNull(addressBook)) {
            throw new OrderBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
        }
        // 写订单表
        Orders orders = createOrder(ordersSubmitDTO, shoppingCartList, addressBook);
        // shoppingCartList ---> orderDetails
        // [购物，购物] -- > [订单详情，订单详情]
        createOrderDetail(shoppingCartList, orders);
        // 购物车清空
        shoppingCartMapper.deleteByUserId(BaseContext.getCurrentId());

        OrderSubmitVO orderSubmitVO = new OrderSubmitVO();
        orderSubmitVO.setId(orders.getId());
        orderSubmitVO.setOrderNumber(orders.getNumber());
        orderSubmitVO.setOrderAmount(orders.getAmount());
        orderSubmitVO.setOrderTime(orders.getOrderTime());
        return orderSubmitVO;
    }

    @Override
    public void payment(OrdersPaymentDTO ordersPaymentDTO) {
        // 使用Http客户端调用微信的接口 https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/mini-prepay.html
        // 解析返回中 prepay_id
        paySuccess(ordersPaymentDTO.getOrderNumber());
    }

    @Override
    public void paySuccess(String number) {
        // 支付完成，修改支付状态
        Orders ordersQuery = new Orders();
        ordersQuery.setNumber(number);
        Orders orders = orderMapper.selectOne(ordersQuery);
        // 幂等
        if (Objects.isNull(orders)) {
            return;
        }
        // 如果当前状态不是待支付，则无需处理
        if (!Objects.equals(Orders.PENDING_PAYMENT, orders.getStatus())) {
            return;
        }
        orders.setStatus(Orders.TO_BE_CONFIRMED);
        orders.setCheckoutTime(LocalDateTime.now());
        orders.setPayStatus(Orders.PAID);
        orderMapper.updateById(orders);
        // 通知管理前端 来订单了 -- 设计这个消息的样式
        HashMap<String, Object> map = new HashMap<>();
        map.put("type", 1);
        map.put("orderId", orders.getId());
        map.put("content", "来订单啦");
        webSocketServer.sendToAllClient(JSON.toJSONString(map));
    }

    /**
     * 创建订单详细信息
     *
     * @param shoppingCartList 购物车列表
     * @param orders           订单
     */
    private void createOrderDetail(List<ShoppingCart> shoppingCartList, Orders orders) {
        List<OrderDetail> orderDetailList = shoppingCartList.stream()
                .map(shoppingCart -> {
                    OrderDetail orderDetail = new OrderDetail();
                    BeanUtils.copyProperties(shoppingCart, orderDetail);
                    orderDetail.setOrderId(orders.getId());
                    return orderDetail;
                }).collect(Collectors.toList());
        orderDetailMapper.insertBatch(orderDetailList);
    }

    /**
     * 创建订单
     *
     * @param ordersSubmitDTO  订单提交dto
     * @param shoppingCartList 购物车列表
     * @param addressBook      通讯簿
     * @return {@link Orders}
     */
    private Orders createOrder(OrdersSubmitDTO ordersSubmitDTO, List<ShoppingCart> shoppingCartList, AddressBook addressBook) {
        Orders orders = new Orders();
        // 订单号：业务的唯一识别码  时间
        long number = System.currentTimeMillis();
        orders.setNumber(String.valueOf(number));
        orders.setStatus(Orders.PENDING_PAYMENT);
        orders.setUserId(BaseContext.getCurrentId());
        orders.setAddressBookId(ordersSubmitDTO.getAddressBookId());
        orders.setOrderTime(LocalDateTime.now());
        orders.setPayMethod(ordersSubmitDTO.getPayMethod());
        orders.setPayStatus(Orders.UN_PAID);
        // 求和
        BigDecimal amount = shoppingCartList.stream()
                .map(shoppingCart -> shoppingCart.getAmount().multiply(new BigDecimal(shoppingCart.getNumber())))
                .reduce(new BigDecimal(0), BigDecimal::add);
        // --------------循环求和--------------------
        // BigDecimal amount = new BigDecimal(0);
        // for (ShoppingCart shoppingCart : shoppingCartList) {
        //     BigDecimal cartAmount = shoppingCart.getAmount();
        //     Integer cartNumber = shoppingCart.getNumber();
        //     amount = amount.add(cartAmount.multiply(new BigDecimal(cartNumber)));
        // }
        // --------------循环求和--------------------
        orders.setAmount(amount);
        orders.setRemark(ordersSubmitDTO.getRemark());
        orders.setPhone(addressBook.getPhone());
        // 省+市+区+详情
        String address = addressBook.getProvinceName() + addressBook.getCityName() + addressBook.getDistrictName() + addressBook.getDetail();
        orders.setAddress(address);
        orders.setConsignee(addressBook.getConsignee());
        orders.setEstimatedDeliveryTime(ordersSubmitDTO.getEstimatedDeliveryTime());
        orders.setDeliveryStatus(ordersSubmitDTO.getDeliveryStatus());
        orders.setPackAmount(ordersSubmitDTO.getPackAmount());
        orders.setTablewareNumber(ordersSubmitDTO.getTablewareNumber());
        orders.setTablewareStatus(ordersSubmitDTO.getTablewareStatus());
        orderMapper.insert(orders);
        return orders;
    }

    @Override
    public PageResult historyOrders(OrdersPageQueryDTO ordersPageQueryDTO) {
        PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());
        Orders orders = new Orders();
        orders.setStatus(ordersPageQueryDTO.getStatus());
        orders.setUserId(BaseContext.getCurrentId());
        Page<Orders> page = orderMapper.selectPage(orders);
        List<Orders> ordersList = page.getResult();

        List<OrderVO> orderVOS = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(ordersList)) {
            List<Long> orderIds = ordersList.stream().map(Orders::getId).collect(Collectors.toList());
            List<OrderDetail> orderDetailList = orderDetailMapper.selectListByOrderIds(orderIds);

            orderVOS = ordersList.stream().map(item -> {
                OrderVO orderVO = new OrderVO();
                BeanUtils.copyProperties(item, orderVO);
                List<OrderDetail> orderDetails = orderDetailList.stream()
                        .filter(orderDetail -> Objects.equals(orderDetail.getOrderId(), item.getId()))
                        .collect(Collectors.toList());
                orderVO.setOrderDetailList(orderDetails);
                return orderVO;
            }).collect(Collectors.toList());
        }
        return new PageResult(page.getTotal(), orderVOS);
    }

    @Override
    public OrderVO orderDetail(Long id) {
        Orders orders = orderMapper.selectById(id);
        List<OrderDetail> orderDetails = orderDetailMapper.selectListByOrderIds(Collections.singletonList(id));
        OrderVO orderVO = new OrderVO();
        BeanUtils.copyProperties(orders, orderVO);
        orderVO.setOrderDetailList(orderDetails);
        return orderVO;
    }

    @Override
    public void cancelById(Long id) {
        Orders orders = new Orders();
        orders.setId(id);
        orders.setStatus(Orders.CANCELLED);
        orders.setPayStatus(2);
        orders.setCancelTime(LocalDateTime.now());
        orderMapper.updateById(orders);
    }

    @Override
    public void repetition(Long id) {
        List<OrderDetail> orderDetailList = orderDetailMapper.selectListByOrderIds(Collections.singletonList(id));
        List<ShoppingCart> shoppingCarts = orderDetailList.stream()
                .map(orderDetail -> {
                    ShoppingCart shoppingCart = new ShoppingCart();
                    BeanUtils.copyProperties(orderDetail, shoppingCart);
                    shoppingCart.setUserId(BaseContext.getCurrentId());
                    shoppingCart.setCreateTime(LocalDateTime.now());
                    return shoppingCart;
                }).collect(Collectors.toList());
        shoppingCartMapper.insertBatch(shoppingCarts);
    }

    @Override
    public PageResult conditionSearch(OrdersPageQueryDTO ordersPageQueryDTO) {
        PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());
        Orders orders = new Orders();
        BeanUtils.copyProperties(ordersPageQueryDTO, orders);
        Page<Orders> page = orderMapper.selectPage(orders);
        return new PageResult(page.getTotal(), page.getResult());
    }

    @Override
    public OrderStatisticsVO statistics() {
        return orderMapper.selectStatistics();
    }

    @Override
    public OrderVO detailsById(Long id) {
        return getOrderVO(id);
    }

    @Override
    public void confirm(OrdersConfirmDTO ordersConfirmDTO) {
        Orders orders = new Orders();
        orders.setId(ordersConfirmDTO.getId());
        orders.setStatus(Orders.CONFIRMED);
        orderMapper.updateById(orders);
    }

    @Override
    public void rejection(OrdersRejectionDTO ordersRejectionDTO) {
        Orders orders = new Orders();
        orders.setId(ordersRejectionDTO.getId());
        orders.setRejectionReason(ordersRejectionDTO.getRejectionReason());
        orders.setStatus(Orders.CONFIRMED);
        orderMapper.updateById(orders);
    }

    @Override
    public void cancel(OrdersCancelDTO ordersCancelDTO) {
        Orders orders = new Orders();
        orders.setId(ordersCancelDTO.getId());
        orders.setCancelReason(ordersCancelDTO.getCancelReason());
        orders.setStatus(Orders.CANCELLED);
        orderMapper.updateById(orders);
    }

    @Override
    public void deliveryById(Long id) {
        Orders orders = new Orders();
        orders.setId(id);
        orders.setStatus(Orders.DELIVERY_IN_PROGRESS);
        orderMapper.updateById(orders);
    }

    @Override
    public void completeById(Long id) {
        Orders orders = new Orders();
        orders.setId(id);
        orders.setStatus(Orders.COMPLETED);
        orderMapper.updateById(orders);
    }

    @Override
    public void reminder(Long orderId) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("type", 2);
        map.put("orderId", orderId);
        map.put("content", "\\单啦~");
        webSocketServer.sendToAllClient(JSON.toJSONString(map));
    }

    @Override
    public TurnoverReportVO turnoverStatistics(LocalDate begin, LocalDate end) {
        // 目标：根据时间区间，获取区间内的每一天的营业额
        // 时间的集合
        ArrayList<LocalDate> localDates = getLocalDates(begin, end);
        // [{time: 1}, {time: 1}, {time: 1}] --> [1, 2, 3, 4, 5]  --> 1,2,3,4,5
        List<Map<String, Object>> result = orderMapper.selectTurnoverStatistics(localDates);
        log.info("时间序列是: {}", localDates);

        String dateList = result.stream()
                .map(item -> (String) item.get("time"))
                .collect(Collectors.joining(","));

        String turnoverList = result.stream()
                .map(item -> (BigDecimal) item.get("amount"))
                .map(BigDecimal::toString)
                .collect(Collectors.joining(","));

        TurnoverReportVO turnoverReportVO = new TurnoverReportVO();
        turnoverReportVO.setDateList(dateList);
        turnoverReportVO.setTurnoverList(turnoverList);
        return turnoverReportVO;
    }

    @Override
    public OrderReportVO ordersStatistics(LocalDate begin, LocalDate end) {
        Map<String, Object> ordersStatisticsTotal = orderMapper.selectOrdersStatisticsTotal(begin, end);

        // 时间的集合
        ArrayList<LocalDate> localDates = getLocalDates(begin, end);
        List<Map<String, Object>> result = orderMapper.selectOrdersStatistics(localDates);

        String dateList = result.stream()
                .map(item -> (String) item.get("time"))
                .collect(Collectors.joining(","));

        String orderCountList = result.stream()
                .map(item -> (Long) item.get("total_orders"))
                .map(Object::toString)
                .collect(Collectors.joining(","));

        String validOrderCountList = result.stream()
                .map(item -> (Long) item.get("validate_orders"))
                .map(Object::toString)
                .collect(Collectors.joining(","));

        Long totalOrders = (Long) ordersStatisticsTotal.get("total_orders");
        Long validateOrders = (Long) ordersStatisticsTotal.get("validate_orders");
        BigDecimal finishRate = (BigDecimal) ordersStatisticsTotal.get("finish_rate");

        OrderReportVO orderReportVO = new OrderReportVO();
        orderReportVO.setDateList(dateList);
        orderReportVO.setOrderCountList(orderCountList);
        orderReportVO.setValidOrderCountList(validOrderCountList);
        orderReportVO.setTotalOrderCount(totalOrders.intValue());
        orderReportVO.setValidOrderCount(validateOrders.intValue());
        orderReportVO.setOrderCompletionRate(finishRate.doubleValue());

        return orderReportVO;
    }

    private static ArrayList<LocalDate> getLocalDates(LocalDate begin, LocalDate end) {
        ArrayList<LocalDate> localDates = new ArrayList<>();
        if (begin.isEqual(end)) {
            localDates.add(begin);
        } else {
            localDates.add(begin);
            LocalDate nextDate = begin.plusDays(1);
            localDates.add(nextDate);
            while (true) {
                nextDate = nextDate.plusDays(1);
                if (nextDate.isAfter(end)) {
                    break;
                }
                localDates.add(nextDate);
            }
        }
        return localDates;
    }

    @Override
    public SalesTop10ReportVO top10(LocalDate begin, LocalDate end) {
        List<Map<String, Object>> maps = orderMapper.selectTop10(begin, end);

        String nameList = maps.stream()
                .map(item -> (String) item.get("name"))
                .collect(Collectors.joining(","));

        String numberList = maps.stream()
                .map(item -> (BigDecimal) item.get("count"))
                .map(BigDecimal::toString)
                .collect(Collectors.joining(","));

        SalesTop10ReportVO salesTop10ReportVO = new SalesTop10ReportVO();
        salesTop10ReportVO.setNameList(nameList);
        salesTop10ReportVO.setNumberList(numberList);

        return salesTop10ReportVO;
    }

    @Override
    public void export() {
        try {
            LocalDate yesterday = LocalDate.now().minusDays(1);
            LocalDate days30Before = yesterday.minusDays(29L);
            // 文件先凑出来
            // 1. 读取磁盘上的excel文件
            FileInputStream inputStream = new FileInputStream("E:\\project\\_63\\sky-take-out\\sky-take-out\\sky-server\\src\\main\\resources\\template\\operationalDataReportTemplate.xlsx");
            Workbook wb = new XSSFWorkbook(inputStream);
            // 2. 修改数据
            Sheet sheetAt = wb.getSheetAt(0);
            Row row2 = sheetAt.getRow(1);
            Row row4 = sheetAt.getRow(3);
            Row row5 = sheetAt.getRow(4);

            Cell cell22 = row2.getCell(1);
            cell22.setCellValue("时间：" + days30Before + "~" + yesterday);
            // 五个值
            StatisticsResult statisticsResult = orderMapper.selectStatisticsResult(days30Before, yesterday);
            row4.getCell(2).setCellValue(statisticsResult.getAmount().toString());
            row4.getCell(4).setCellValue(statisticsResult.getRate().toString());
            row4.getCell(6).setCellValue(statisticsResult.getNewUserCount());
            row5.getCell(2).setCellValue(statisticsResult.getValidCount());
            row5.getCell(4).setCellValue(statisticsResult.getAvgAmount().toString());

            // 生成时间序列
            ArrayList<LocalDate> localDates = getLocalDates(days30Before, yesterday);
            List<StatisticsResult> statisticsResults = orderMapper.selectStatisticsResults(localDates);

            // 第八航 第二列
            for (int i = 7; i < 37; i++) {
                StatisticsResult result = statisticsResults.get(i - 7);
                // 获取行
                Row row = sheetAt.getRow(i);
                for (int j = 1; j < 7; j++) {
                    Cell cell = row.getCell(j);
                    // j = 1 日期
                    if (j == 1) {
                        cell.setCellValue(result.getTime());
                    }
                    if (j == 2) {
                        cell.setCellValue(result.getAmount().toString());
                    }
                    if (j == 3) {
                        cell.setCellValue(result.getValidCount());
                    }
                    if (j == 4) {
                        cell.setCellValue(result.getRate().doubleValue());
                    }
                    if (j == 5) {
                        cell.setCellValue(result.getAvgAmount().doubleValue());
                    }
                    if (j == 6) {
                        cell.setCellValue(result.getNewUserCount());
                    }
                }
            }


            // 3. 走下载
            ServletOutputStream outputStream = httpServletResponse.getOutputStream();
            wb.write(outputStream);

            // 释放资源
            inputStream.close();
            wb.close();
            outputStream.close();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    private OrderVO getOrderVO(Long id) {
        Orders orders = orderMapper.selectById(id);
        List<OrderDetail> orderDetails = orderDetailMapper.selectListByOrderIds(Collections.singletonList(id));
        OrderVO orderVO = new OrderVO();
        BeanUtils.copyProperties(orders, orderVO);
        orderVO.setOrderDetailList(orderDetails);
        return orderVO;
    }
}
